
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Net;
using EasyStorage;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using EventFlowLibrary.Player;
using Microsoft.Xna.Framework.Input;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System;
using EventFlowLibrary.LevelComponents;

namespace ScreenManagement
{
    /// <summary>
    /// The pause menu comes up over the top of the game,
    /// giving the player options to resume or quit.
    /// </summary>
    class LoadGameScreen : MenuScreen
    {
        enum CurrentStatus
        {
            LoadingFiles = -1,
            NoFiles = 0,
            FilesAvailable = 1,
            DeletingGame = 2,
            LoadingGame = 3,
        }
        CurrentStatus currentStatus = CurrentStatus.LoadingFiles;

        SpriteFont spriteFont;

        Texture2D gradient, aButton, bButton, yButton;
        Texture2D loadTexture;

        /// <summary>
        /// The longest file name, for spacing out the left hand side "<<"
        /// </summary>
        float longestFileName = 0;

        /// <summary>
        /// Whether to currently draw a screenshot. Keeps have copied uints from drawing.
        /// </summary>
        bool drawScreenShot = false;

        /// <summary>
        /// TotalGameTime.TotalMilliseconds when the screen is loaded. 
        /// We need this to subtract from so we get an accurate count when tracking time played.
        /// We cannot reset totalgametime.
        /// </summary>
        public double startTotalSeconds;

        /// <summary>
        /// Track our variables for load.
        /// </summary>
        LoadVariables loadVariables;

        /// <summary>
        /// Currently retrieved list of file names
        /// </summary>
        List<string> savedFileNames = new List<string>();

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public LoadGameScreen()
            : base("Load Game")
        { }

        public override void LoadContent()
        {
            ContentManager content = new ContentManager(ScreenManager.Game.Services, "Content"); ;

            spriteFont = content.Load<SpriteFont>("Fonts/gameFontSmall");
            aButton = content.Load<Texture2D>("Textures/Buttons/xboxControllerButtonA");
            bButton = content.Load<Texture2D>("Textures/Buttons/xboxControllerButtonB");
            yButton = content.Load<Texture2D>("Textures/Buttons/xboxControllerButtonY");
            gradient = content.Load<Texture2D>("Textures/Screens/Gradient");

            if (this.ScreenManager.SaveDevice.IsReady && currentStatus == CurrentStatus.LoadingFiles)
            {
                this.ScreenManager.SaveDevice.GetFilesAsync("FromTheSeaPt1");
                this.ScreenManager.SaveDevice.GetFilesCompleted -= SaveDevice_GetFilesCompleted;
                this.ScreenManager.SaveDevice.GetFilesCompleted += new GetFilesCompletedEventHandler(SaveDevice_GetFilesCompleted);
            }
            else
            {
                MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Save device is not ready. Please retry." }, true, false, true, true);
                confirmExitMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);
            }

            base.LoadContent();
        }

        #endregion

        #region Handle Input

        void SaveDevice_GetFilesCompleted(object sender, GetFilesCompletedEventArgs e)
        {
            savedFileNames.Clear();

            if (e.Error != null)
            {
                MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Error loading files: ", e.Error.Message }, true, false, true, true);
                confirmExitMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);

                return;
            }

            if (e.Result.Length > 0)
            {
                currentStatus = CurrentStatus.FilesAvailable;

                longestFileName = this.ScreenManager.FontLarge.MeasureString(e.Result[e.Result.Length - 1]).X;

                for (int i = e.Result.Length - 1; i >= 0; i--)
                {
                    if (longestFileName < this.ScreenManager.FontLarge.MeasureString(e.Result[i]).X)
                        longestFileName = this.ScreenManager.FontLarge.MeasureString(e.Result[i]).X;

                    // Track our file name to load
                    savedFileNames.Add(e.Result[i].ToString());
                }
            }
            else
            {
                currentStatus = CurrentStatus.NoFiles;
            }

            // Handle deletions that remove an entire page of results
            if (startIndex >= savedFileNames.Count)
            {
                startIndex -= maxEntries;
                if (startIndex < 0)
                    startIndex = 0;
            }

            // Only show N number at a time
            MenuEntries.Clear();
            for (int i = startIndex; i < startIndex + maxEntries && i < savedFileNames.Count; i++)
            {
                MenuEntry gameName = new MenuEntry(savedFileNames[i].ToString(), 1);
                gameName.Selected += LoadSelectedLevel;
                MenuEntries.Add(gameName);
            }

            // Make sure selected works properly
            if (SelectedEntry >= MenuEntries.Count)
            {
                SelectedEntry = MenuEntries.Count - 1;
            }

            // Show the screenshot
            if (this.MenuEntries.Count == 0)
            {
                drawScreenShot = false;
                this.loadTexture = null;
            }
            else
            {
                LoadInfo();
            }
        }

        private void LoadInfo()
        {
            drawScreenShot = false;

            if (this.ScreenManager.SaveDevice.IsReady)
            {
                this.ScreenManager.SaveDevice.LoadAsync("FromTheSeaPt1", this.MenuEntries[(this.SelectedEntry == -1 ? 0 : this.SelectedEntry)].Text,
                    stream =>
                    {
                        using (BinaryReader reader = new BinaryReader(stream))
                        {
                            // Read screenshot
                            uint[] colorData = new uint[reader.ReadInt32()];
                            for (int i = 0; i < colorData.Length; i++)
                            {
                                colorData[i] = reader.ReadUInt32();
                            }
                            // Init our class.
                            loadVariables = new LoadVariables();

                            loadVariables.level = reader.ReadString();                          // Read xml level
                            loadVariables.position.X = (float)reader.ReadDouble();              // Read character position
                            loadVariables.position.Y = (float)reader.ReadDouble();
                            loadVariables.position.Z = (float)reader.ReadDouble();
                            loadVariables.orientation.X = (float)reader.ReadDouble();           // Read character orientation
                            loadVariables.orientation.Y = (float)reader.ReadDouble();
                            loadVariables.orientation.Z = (float)reader.ReadDouble();
                            loadVariables.orientation.W = (float)reader.ReadDouble();
                            loadVariables.cameraPitch = (float)reader.ReadDouble();             // Read camera pitch
                            loadVariables.cameraYaw = (float)reader.ReadDouble();               // Read camera yaw
                            loadVariables.totalRotationY = (float)reader.ReadDouble();          //Read total Y rotation
                            loadVariables.timePlayed = reader.ReadDouble();                     // Read time played
                            loadVariables.saved = DateTime.FromBinary(reader.ReadInt64());      // Read time saved

                            loadTexture = new Texture2D(this.ScreenManager.GraphicsDevice, 480, 270);
                            loadTexture.SetData<uint>(colorData);

                            drawScreenShot = true;
                        }
                    });
            }
            else
            {
                MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Save device is not ready. Please retry." }, true, false, true, true);
                confirmExitMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);
            }
        }

        void SaveDevice_DeleteCompleted(object sender, FileActionCompletedEventArgs e)
        {
            currentStatus = CurrentStatus.LoadingFiles;

            if (e.Error != null)
            {
                MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Error loading files: ", e.Error.Message }, true, false, true, true);
                confirmExitMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);

                return;
            }

            if (this.ScreenManager.SaveDevice.IsReady && currentStatus == CurrentStatus.LoadingFiles)
            {
                this.ScreenManager.SaveDevice.GetFilesAsync("FromTheSeaPt1");
            }
            else
            {
                MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Save device is not ready. Please retry." }, true, false, true, true);
                confirmExitMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);
            }
        }

        /// <summary>
        /// Event handler for when the user selects ok on the "are you sure
        /// you want to delete" message box.
        /// </summary>
        void ConfirmDeleteMessageBoxAccepted(object sender, PlayerIndexEventArgs e)
        {
            currentStatus = CurrentStatus.DeletingGame;

            if (this.ScreenManager.SaveDevice.IsReady)
            {
                this.drawScreenShot = false;
                this.ScreenManager.SaveDevice.DeleteAsync("FromTheSeaPt1", this.MenuEntries[this.SelectedEntry].Text);
                this.ScreenManager.SaveDevice.DeleteCompleted -= SaveDevice_DeleteCompleted;
                this.ScreenManager.SaveDevice.DeleteCompleted += new DeleteCompletedEventHandler(SaveDevice_DeleteCompleted);

                this.savedFileNames.Clear();
                this.MenuEntries.Clear();
            }
            else
            {
                MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Save device is not ready. Please retry." }, true, false, true, true);
                confirmExitMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);
            }
        }

        /// <summary>
        /// Event handler for when the user selects ok on the "are you sure
        /// you want to quit" message box. This uses the loading screen to
        /// transition from the game back to the main menu screen.
        /// </summary>
        void LoadSelectedLevel(object sender, PlayerIndexEventArgs e)
        {
            if (loadVariables != null)
            {
                currentStatus = CurrentStatus.LoadingGame;

                LoadingScreen.Load(ScreenManager, true, this.ControllingPlayer, new GameplayScreen(startTotalSeconds, loadVariables, this.ScreenManager.gameSettings));
            }
            else
            {
                MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "File could not load. Please retry." }, true, false, true, true);
                confirmExitMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);
            }
        }

        #endregion

        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            startTotalSeconds = gameTime.TotalGameTime.TotalMilliseconds;

            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
        }

        public override void HandleInput(GameTime _gameTime, InputState input)
        {
            // Do this up top so correct image shows
            base.HandleInput(_gameTime, input);

            // Delete 
            if (this.MenuEntries.Count > 0)
            {
                if (input.IsNewButtonPress(Buttons.Y, this.ControllingPlayer.Value) || input.IsNewKeyPress(Keys.Y, this.ControllingPlayer.Value))
                {
                    MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Are you sure you want to delete this saved game?" }, true, true, true, true);
                    confirmExitMessageBox.Accepted += ConfirmDeleteMessageBoxAccepted;
                    ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);
                }
                // Change displayed files
                else if (input.IsNewButtonPress(Buttons.RightShoulder, this.ControllingPlayer.Value) || input.IsNewButtonPress(Buttons.RightTrigger, this.ControllingPlayer.Value) ||
                         input.IsNewButtonPress(Buttons.DPadRight, this.ControllingPlayer.Value) || input.IsNewButtonPress(Buttons.LeftThumbstickRight, this.ControllingPlayer.Value) ||
                         input.IsNewButtonPress(Buttons.RightThumbstickRight, this.ControllingPlayer.Value) || input.IsNewKeyPress(Keys.Right, this.ControllingPlayer.Value))
                {
                    if (this.startIndex + this.maxEntries >= savedFileNames.Count)
                        return;

                    this.startIndex += this.maxEntries;

                    MenuEntries.Clear();

                    for (int i = startIndex; i < startIndex + maxEntries && i < savedFileNames.Count; i++)
                    {
                        MenuEntry gameName = new MenuEntry(savedFileNames[i].ToString(), 1);
                        gameName.Selected += LoadSelectedLevel;
                        MenuEntries.Add(gameName);
                    }

                    this.SelectedEntry = 0;
                }
                else if (input.IsNewButtonPress(Buttons.LeftShoulder, this.ControllingPlayer.Value) || input.IsNewButtonPress(Buttons.LeftTrigger, this.ControllingPlayer.Value) ||
                         input.IsNewButtonPress(Buttons.DPadLeft, this.ControllingPlayer.Value) || input.IsNewButtonPress(Buttons.LeftThumbstickLeft, this.ControllingPlayer.Value) ||
                         input.IsNewButtonPress(Buttons.RightThumbstickLeft, this.ControllingPlayer.Value) || input.IsNewKeyPress(Keys.Left, this.ControllingPlayer.Value))
                {
                    if (this.startIndex - this.maxEntries < 0)
                        return;

                    this.startIndex -= this.maxEntries;

                    MenuEntries.Clear();

                    for (int i = startIndex; i < startIndex + maxEntries && i < savedFileNames.Count; i++)
                    {
                        MenuEntry gameName = new MenuEntry(savedFileNames[i].ToString(), 1);
                        gameName.Selected += LoadSelectedLevel;
                        MenuEntries.Add(gameName);
                    }

                    this.SelectedEntry = 0;
                }
                // Show the screenshots
                else if ((input.IsMenuUp(this.ControllingPlayer.Value) || input.IsMenuDown(this.ControllingPlayer.Value)) && MenuEntries.Count > 1)
                {
                    if (this.ScreenManager.SaveDevice.IsReady)
                    {
                        LoadInfo();
                    }
                }
            }
        }

        public override void Draw(GameTime gameTime)
        {
            if (!IsExiting)
            {
                int width = this.ScreenManager.GraphicsDevice.Viewport.Width;
                int height = this.ScreenManager.GraphicsDevice.Viewport.Height;

                this.ScreenManager.SpriteBatch.Begin();

                // All buttons are drawn 32x32 with a 10px buffer between text (42 + text length total)
                if (currentStatus == CurrentStatus.LoadingFiles)
                {
                    // Draw "Loading..."
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Loading...", new Vector2((width / 2) - (spriteFont.MeasureString("Loading...").X / 2) + 2, 202), Color.Black);
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Loading...", new Vector2((width / 2) - (spriteFont.MeasureString("Loading...").X / 2), 200), Color.White);
                }
                else if (currentStatus == CurrentStatus.NoFiles)
                {
                    // Draw "No saved games..."
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "No Saved Games Found...", new Vector2((width / 2) - (spriteFont.MeasureString("No Saved Games Found...").X / 2) + 2, 202), Color.Black);
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "No Saved Games Found...", new Vector2((width / 2) - (spriteFont.MeasureString("No Saved Games Found...").X / 2), 200), Color.White);
                }
                else if (currentStatus == CurrentStatus.DeletingGame)
                {
                    // Draw "No saved games..."
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Deleting Game...", new Vector2((width / 2) - (spriteFont.MeasureString("Deleting Game...").X / 2) + 2, 202), Color.Black);
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Deleting Game...", new Vector2((width / 2) - (spriteFont.MeasureString("Deleting Game...").X / 2), 200), Color.White);
                }
                else if (currentStatus == CurrentStatus.LoadingGame)
                {
                    // Draw "No saved games..."
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Loading Game...", new Vector2((width / 2) - (spriteFont.MeasureString("Loading Game...").X / 2) + 2, 202), Color.Black);
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Loading Game...", new Vector2((width / 2) - (spriteFont.MeasureString("Loading Game...").X / 2), 200), Color.White);
                }
                else if (currentStatus == CurrentStatus.FilesAvailable && this.ScreenManager.SaveDevice.IsReady)
                {
                    // Draw saved games...
                    // Draw "Select" button
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Select", new Vector2((width * 0.9f) - spriteFont.MeasureString("Select").X - 42 + 2, (int)(height * 0.9f) - 30 + 2), Color.Black);
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Select", new Vector2((width * 0.9f) - spriteFont.MeasureString("Select").X - 42, (int)(height * 0.9f) - 30), Color.White);
                    this.ScreenManager.SpriteBatch.Draw(aButton, new Rectangle((int)((width * 0.9f) - 32), (int)((height * 0.9f) - 32), 32, 32), Color.White);

                    // Draw "Delete" button
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Delete", new Vector2((width * 0.5f) - ((spriteFont.MeasureString("Delete").X + 42 + 2) / 2), (int)(height * 0.9f) - 30 + 2), Color.Black);
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Delete", new Vector2((width * 0.5f) - ((spriteFont.MeasureString("Delete").X + 42) / 2), (int)(height * 0.9f) - 30), Color.White);
                    this.ScreenManager.SpriteBatch.Draw(yButton, new Rectangle((int)((width * 0.5f) + ((spriteFont.MeasureString("Delete").X) - 84 / 2)), (int)((height * 0.9f) - 32), 32, 32), Color.White);

                    // Draw scroll or page icons
                    if (startIndex != 0)
                    {
                        // Draw left option
                        this.ScreenManager.SpriteBatch.DrawString(this.ScreenManager.FontLarge, "<<", new Vector2((width * 0.9f) - (int)longestFileName + 2, (int)(height * 0.5f) - (this.ScreenManager.FontLarge.LineSpacing / 2) + 2), Color.Black);
                        this.ScreenManager.SpriteBatch.DrawString(this.ScreenManager.FontLarge, "<<", new Vector2((width * 0.9f) - (int)longestFileName, (int)(height * 0.5f) - (this.ScreenManager.FontLarge.LineSpacing / 2)), Color.White);
                    }
                    if (savedFileNames.Count > startIndex + maxEntries)
                    {
                        // Draw right option
                        this.ScreenManager.SpriteBatch.DrawString(this.ScreenManager.FontLarge, ">>", new Vector2((width * 0.9f) - this.ScreenManager.FontLarge.MeasureString(">>").X + 2, (int)(height * 0.5f) - (this.ScreenManager.FontLarge.LineSpacing / 2) + 2), Color.Black);
                        this.ScreenManager.SpriteBatch.DrawString(this.ScreenManager.FontLarge, ">>", new Vector2((width * 0.9f) - this.ScreenManager.FontLarge.MeasureString(">>").X, (int)(height * 0.5f) - (this.ScreenManager.FontLarge.LineSpacing / 2)), Color.White);
                    }
                }

                if (currentStatus != CurrentStatus.LoadingGame && currentStatus != CurrentStatus.DeletingGame)
                {
                    // Draw "Back" button
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Back", new Vector2((width * 0.1f) + 2, (int)(height * 0.9f) - 30 + 2), Color.Black);
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Back", new Vector2((width * 0.1f), (int)(height * 0.9f) - 30), Color.White);
                    this.ScreenManager.SpriteBatch.Draw(bButton, new Rectangle((int)((width * 0.1f) + spriteFont.MeasureString("Back").X) + 10, (int)((height * 0.9f) - 32), 32, 32), Color.White);
                }

                // Draw screenshot from the world
                if (drawScreenShot && loadVariables != null)
                {
                    // Give 4 pixel black border, 1 px colored
                    this.ScreenManager.SpriteBatch.Draw(gradient, new Rectangle((int)(width * 0.1f) - 4, (int)(height * 0.5f) - 139, 488, 278), Color.Black);
                    this.ScreenManager.SpriteBatch.Draw(gradient, new Rectangle((int)(width * 0.1f) - 1, (int)(height * 0.5f) - 136, 482, 272), Color.White);
                    this.ScreenManager.SpriteBatch.Draw(loadTexture, new Rectangle((int)(width * 0.1f), (int)(height * 0.5f) - 135, 480, 270), Color.White);

                    // Draw the saved time, 2px from bottom of border
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Saved: " + loadVariables.saved.ToString(), new Vector2((int)(width * 0.1f) + (488 / 2) - (spriteFont.MeasureString("Saved: " + loadVariables.saved.ToString()).X / 2), (int)(height * 0.5f) + (280 / 2)), Color.White);
                    // Draw the saved time, 2px from bottom of border and under saved
                    this.ScreenManager.SpriteBatch.DrawString(spriteFont, "Played: " + TimeSpan.FromMilliseconds(loadVariables.timePlayed).ToString(@"dd\.hh\:mm\:ss"), new Vector2((int)(width * 0.1f) + (488 / 2) - (spriteFont.MeasureString("Played: " + TimeSpan.FromMilliseconds(loadVariables.timePlayed).ToString(@"dd\.hh\:mm\:ss")).X / 2), (int)(height * 0.5f) + (280 / 2) + spriteFont.LineSpacing), Color.White);
                }

                this.ScreenManager.SpriteBatch.End();
            }

            base.Draw(gameTime);
        }
    }
}
